package cn.hg.solon.youcan.system.provider;

import cn.hg.solon.youcan.common.constant.AppConstants;
import cn.hg.solon.youcan.common.constant.WebConstants;
import cn.hg.solon.youcan.common.enums.BeanStatus;
import cn.hg.solon.youcan.common.exception.ServiceException;
import cn.hg.solon.youcan.flex.util.QueryWrapperUtil;
import cn.hg.solon.youcan.system.entity.Permission;
import cn.hg.solon.youcan.system.entity.Role;
import cn.hg.solon.youcan.system.entity.SysPermission;
import cn.hg.solon.youcan.system.entity.User;
import cn.hg.solon.youcan.system.mapper.SysPermissionMapper;
import cn.hg.solon.youcan.system.mapper.SysRolePermissionMappingMapper;
import cn.hg.solon.youcan.system.service.PermissionService;
import com.mybatisflex.core.query.*;
import com.mybatisflex.solon.service.impl.ServiceImpl;
import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.text.StrValidator;
import org.dromara.hutool.core.tree.MapTree;
import org.dromara.hutool.core.tree.TreeNodeConfig;
import org.dromara.hutool.core.tree.TreeUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.db.sql.Order;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.data.annotation.Tran;

import java.util.List;
import java.util.Map;

import static cn.hg.solon.youcan.system.entity.table.SysPermissionTableDef.SYS_PERMISSION;
import static cn.hg.solon.youcan.system.entity.table.SysRolePermissionMappingTableDef.SYS_ROLE_PERMISSION_MAPPING;
import static cn.hg.solon.youcan.system.entity.table.SysRoleTableDef.SYS_ROLE;
import static cn.hg.solon.youcan.system.entity.table.SysUserRoleMappingTableDef.SYS_USER_ROLE_MAPPING;

/**
 * @author 胡高
 */
@Component
public class SysPermissionProvider extends ServiceImpl<SysPermissionMapper, SysPermission>
        implements PermissionService {

    @Inject
    private SysRolePermissionMappingMapper rolePermissionMappingMapper;

    /**
     * <pre>
     * SELECT p.*
     * FROM sys_permission AS p
     * WHERE #if(status)p.status = #para(status)#else p.status <> 'DEL'#end
     *     #if(type)AND p.`type`=#para(type)#end
     *     #if(word)AND (p.`name` #likePara(word) OR  p.`value` #likePara(word))#end
     *     #if(word || type)OR FIND_IN_SET(p.id, (
     *         SELECT GROUP_CONCAT(p2.ancestors)
     *         FROM sys_permission AS p2
     *         WHERE #if(status)p2.status = #para(status)#else p2.status <> 'DEL'#end
     *             #if(type)AND p2.`type`=#para(type)#end
     *             #if(word)AND (p2.`name` #likePara(word) OR  p2.`value` #likePara(word))#end
     *     ))#end
     * </pre>
     */
    private QueryWrapper buildQuery(Map<String, Object> paraMap) {
        String word = (String) paraMap.get("word");
        String type = (String) paraMap.get("type");
        String status = (String) paraMap.get("status");

        QueryCondition findInSet = new FindInSetQueryCondition("OR", SYS_PERMISSION.ID, QueryWrapper.create()
                .select(new FunctionQueryColumn("GROUP_CONCAT", SYS_PERMISSION.ANCESTORS))
                .from(SYS_PERMISSION)
                .where(SYS_PERMISSION.STATUS.eq(status).when(StrValidator.isNotEmpty(status))
                        .and(SYS_PERMISSION.TYPE.eq(type).when(StrValidator.isNotEmpty(type)))
                        .and(SYS_PERMISSION.NAME.like(word).when(StrValidator.isNotEmpty(word))
                                .or(SYS_PERMISSION.VALUE.like(word).when(StrValidator.isNotEmpty(word)))
                        )
                ))
                .when(StrValidator.isNotEmpty(word) || StrValidator.isNotEmpty(type));

        QueryWrapper query = QueryWrapper.create()
                .from(SYS_PERMISSION)
                .where(SYS_PERMISSION.STATUS.eq(status).when(StrValidator.isNotEmpty(status))
                        .and(SYS_PERMISSION.TYPE.eq(type).when(StrValidator.isNotEmpty(type)))
                        .and(SYS_PERMISSION.NAME.like(word).when(StrValidator.isNotEmpty(word))
                                .or(SYS_PERMISSION.VALUE.like(word).when(StrValidator.isNotEmpty(word)))
                        )
                        .or(findInSet)
                );

        return QueryWrapperUtil.applyOrderBy(query, new Order(SYS_PERMISSION.SORT.getName()));
    }

    /**
     * <pre>
     * SELECT DISTINCT p.*
     * FROM sys_permission AS p
     *     LEFT JOIN sys_role_permission_mapping AS pm ON pm.permission_id = p.id
     * WHERE pm.role_id = #para(roleId)
     * </pre>
     */
    private QueryWrapper buildQueryByRole(Role bean) {
        QueryWrapper query = QueryWrapper.create().select(new DistinctQueryColumn(SYS_PERMISSION.ALL_COLUMNS))
                .leftJoin(SYS_ROLE_PERMISSION_MAPPING).on(SYS_ROLE_PERMISSION_MAPPING.PERMISSION_ID.eq(SYS_PERMISSION.ID))
                .where(SYS_ROLE_PERMISSION_MAPPING.ROLE_ID.eq(bean.getId()))
                .orderBy(new QueryOrderBy(new QueryColumn(SYS_PERMISSION.SORT.getName()), WebConstants.ORDER_DIRECTION_ASC));
        return query;
    }

    /**
     * <pre>
     * SELECT DISTINCT p.*
     * FROM sys_permission AS p
     *     LEFT JOIN sys_role_permission_mapping AS rp ON rp.permission_id = p.id
     *     LEFT JOIN sys_user_role_mapping AS ur ON ur.role_id = rp.role_id
     * WHERE #if(status)p.status = #para(status)#else p.status <> 'ON'#end
     *     #if(type)AND p.`type`=#para(type)#end
     *     #if(userId && userId > 1)AND ur.user_id = #para(userId)#end
     * </pre>
     */
    private QueryWrapper buildQueryByUser(User bean) {
        QueryWrapper query = QueryWrapper.create().select(new DistinctQueryColumn(SYS_PERMISSION.ALL_COLUMNS))
                .leftJoin(SYS_ROLE_PERMISSION_MAPPING).on(SYS_ROLE_PERMISSION_MAPPING.PERMISSION_ID.eq(SYS_PERMISSION.ID))
                .leftJoin(SYS_USER_ROLE_MAPPING).on(SYS_USER_ROLE_MAPPING.ROLE_ID.eq(SYS_ROLE_PERMISSION_MAPPING.ROLE_ID))
                .where(SYS_PERMISSION.STATUS.eq(BeanStatus.ON.name())
                        .and(SYS_USER_ROLE_MAPPING.USER_ID.eq(bean.getId()).when(ObjUtil.isNotNull(bean)))
                )
                .orderBy(new QueryOrderBy(new QueryColumn(SYS_PERMISSION.SORT.getName()), WebConstants.ORDER_DIRECTION_ASC));
        return query;
    }

    @Override
    public boolean checkUnique(Permission bean) {
        // 查找已存在记录
        QueryWrapper query = QueryWrapper.create()
                .where(SYS_PERMISSION.NAME.eq(bean.getName())
                        .and(SYS_PERMISSION.PARENT_ID.eq(bean.getParentId()))
                        .and(SYS_PERMISSION.ID.ne(bean.getId()))
                );

        return ObjUtil.isNull(this.getOne(query));
    }

    @Tran
    @Override
    public boolean delete(Integer id) {
        if (this.getMapper().selectCountByCondition(SYS_PERMISSION.PARENT_ID.eq(id)) > 0) {
            throw new ServiceException("存在子项，不允许删除！");
        }

        if (this.rolePermissionMappingMapper.selectCountByQuery(QueryWrapper.create()
                .leftJoin(SYS_ROLE).on(SYS_ROLE.ID.eq(SYS_ROLE_PERMISSION_MAPPING.ROLE_ID))
                .where(SYS_ROLE_PERMISSION_MAPPING.PERMISSION_ID.eq(id))
        ) > 0) {
            throw new ServiceException("已分配于角色，不允许删除！");
        }

        return this.getMapper().deleteById(id) > 0;
    }

    @Override
    public SysPermission get(Integer id) {
        return this.getMapper().selectOneById(id);
    }

    @Tran
    @Override
    public boolean insert(Permission bean) {
        /*
         * 无父对象ID时，默认为0
         */
        if (ObjUtil.isNull(bean.getParentId())) {
            bean.setParentId(0);
        }

        if (!this.checkUnique(bean)) {
            throw new ServiceException("权限名称已经存在，请更换其它值！");
        }

        Permission partner = this.get(bean.getParentId());
        bean.setAncestors(ObjUtil.isNull(partner) ? "0" : partner.getAncestors() + "," + bean.getParentId());

        SysPermission cloneBean = BeanUtil.copyProperties(bean, SysPermission.class);

        return this.getMapper().insert(cloneBean) > 0;
    }

    @Override
    public List<SysPermission> listBy(Map<String, Object> paraMap) {
        return this.list(this.buildQuery(paraMap));
    }

    @Override
    public List<SysPermission> listByRole(Role bean) {
        if (ObjUtil.isNull(bean)) {
            return ListUtil.empty();
        }

        if (bean.getId() == 1) {
            return this.list(QueryWrapper.create().where(SYS_PERMISSION.STATUS.eq(BeanStatus.ON.name()))
                    .orderBy(new QueryOrderBy(new QueryColumn(SYS_PERMISSION.SORT.getName()), WebConstants.ORDER_DIRECTION_ASC)));
        }

        return this.list(this.buildQueryByRole(bean));
    }

    @Override
    public List<MapTree<Integer>> treeBy(Map<String, Object> paraMap) {
        List<SysPermission> list = this.listBy(paraMap);

        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名 都要默认值的
        treeNodeConfig.setIdKey("id");
        treeNodeConfig.setParentIdKey("parentId");
        treeNodeConfig.setNameKey("name");

        List<MapTree<Integer>> trees = TreeUtil.build(list, 0, treeNodeConfig, (treeNode, tree) -> {
            tree.setId((Integer) treeNode.getId());
            tree.setParentId((Integer) treeNode.getParentId());
            tree.setName((String) treeNode.getName());
            //扩展属性
            tree.putExtra("disabled", !ObjUtil.equals(BeanStatus.ON.name(), treeNode.getStatus()));
        });

        return trees;
    }

    @Override
    public List<SysPermission> listByUser(User bean) {
        if (ObjUtil.isNull(bean)) {
            return ListUtil.empty();
        }

        if (bean.getId() == 1) {
            return this.list(QueryWrapper.create().where(SYS_PERMISSION.STATUS.eq(BeanStatus.ON.name()))
                    .orderBy(new QueryOrderBy(new QueryColumn(SYS_PERMISSION.SORT.getName()), WebConstants.ORDER_DIRECTION_ASC)));
        }

        return this.list(this.buildQueryByUser(bean));
    }

    @Tran
    @Override
    public boolean update(Permission bean) {
        /*
         * 无父对象ID时，默认为0
         */
        if (ObjUtil.isNull(bean.getParentId())) {
            bean.setParentId(0);
        }
        Integer newParentId = bean.getParentId();

        if (!this.checkUnique(bean)) {
            throw new ServiceException("权限名称已经存在，请更换其它值！");
        }

        SysPermission parent = this.get(bean.getParentId());
        bean.setAncestors(ObjUtil.isNull(parent) ? "0" : parent.getAncestors() + "," + bean.getParentId());

        SysPermission cloneBean = this.get(bean.getId());
        Integer currentParentId = cloneBean.getParentId();
        cloneBean = BeanUtil.copyProperties(bean, SysPermission.class);

        boolean ret = this.getMapper().update(cloneBean) > 0;

        // 如果上级部门有变化，则更新所有下级部门的ancestors字段
        if (!ObjUtil.equals(newParentId, currentParentId)) {
            this.updateChildrenAncestors(cloneBean);
        }

        return ret;
    }

    private void updateChildrenAncestors(SysPermission bean) {
        // 查询所有直接下级
        List<SysPermission> childrenList = this.getMapper().selectListByCondition(SYS_PERMISSION.PARENT_ID.eq(bean.getId()));

        // 更新下级的ancestors字段
        for (SysPermission item : childrenList) {
            item.setAncestors(bean.getAncestors() + "," + item.getParentId());
            this.getMapper().update(item);

            // 递归更新再下一级
            this.updateChildrenAncestors(item);
        }
    }
}
